package agents;

import java.util.HashMap;
import java.util.Random;

import environment.Casilla;
import environment.Tablero;
import jade.core.Agent;
import jade.core.behaviours.TickerBehaviour;

public class AgenteDeliberativo extends Agent{
	
	
	
	public static final int CARGADOR=1;
	public static final int ENTREGAAZUL=2;
	public static final int ENTREGAROJO=3;
	public static final int ENTREGAVERDE=4;
	public static final int ENTREGAAMARILLO=5;
	public static final int PAQUETEAZUL=6;
	public static final int PAQUETEROJO=7;
	public static final int PAQUETEVERDE=8;
	public static final int PAQUETEAMARILLO=9;
	public static final int AGENTE=10;
	public static final int AGENTECARGADOR=11;
	
	private static final int INICIAL=0;
	private static final int LLEVANDOPAQUETE=1;
	private static final int LLEVANDOPAQUETEAPUNTO=2;
	private static final int YENDOACARGADOR=3;
	private static final int CARGADORAVISTADO=4;
	private static final int CARGANDO=5;
	private static final int YENDOAPAQUETE=6;
	private static final int BUSCANDO=7;
	
	
	private int state;
	private int energy;
	private int usedEnergy;
	private int maxenergy;
	private int carrying;
	private HashMap<Integer,Casilla> memoria = new HashMap<Integer, Casilla>();
	private Casilla destino;
	private Tablero tablero;
	private int tickSpeed;
	private Casilla situation;
	private Casilla[][] vision; 
	 
	/**
	 * Memoriza paquetes y puntos de entrega
	 */
	private void checkEnviroment()
	{
		
		for(Casilla[] casillas : vision)
			for(Casilla c : casillas){
				if(c!=null)
				{
					switch (c.getContenido()) {
					case ENTREGAAZUL:
							memoria.put(ENTREGAAZUL, c);
							break;
					case ENTREGAROJO:
						memoria.put(ENTREGAROJO, c);
						break;
					case ENTREGAAMARILLO:
						memoria.put(ENTREGAAMARILLO, c);
						break;
					case ENTREGAVERDE:
						memoria.put(ENTREGAVERDE, c);
						break;
					case AGENTE:
						break;
					case AGENTECARGADOR:
						break;
					case 0:
						break;
					default:
						memoria.put(c.hashCode(), c);
						break;
					}
				}
			}
	}
	
	
	private Casilla firstPacket(){
		int d = 10000;
		Casilla des = null;
		for(Casilla c : memoria.values()){
			if(c.getContenido()>5 && c.getContenido()<10)
			{
				if(distancia(situation, c)<d)
				{
					d = distancia(situation, c);
					des = c;
				}
			}
		}
		return des;
	}
	
	private void move(Casilla newpos) {
		System.out.println(this.getName() + " tries to move to " + newpos.coordsStr());
		if (newpos.getContenido() < 2) {
			//SI LA CASILLA ESTA VACIA O HAY UN CARGADOR, ME PUEDO MOVER
			if (carrying == 0) {
				this.energy = this.energy - 1;
				this.usedEnergy = this.usedEnergy + 1;
			} else {
				this.energy = this.energy - 2;
				this.usedEnergy = this.usedEnergy + 2;
			}
			this.situation.setAgentLoad(0);
			this.situation.setContenido(this.situation.getContenido() - 10);
			this.situation.updateGui();
			newpos.setAgentLoad(this.carrying);
			newpos.setContenido(newpos.getContenido() + 10);
			newpos.updateGui();
			this.situation = newpos;
			System.out.println(this.getName() + " successfully moved to " + newpos.coordsStr());			
		} else {
			//Y SI NO, ME QUEDO EN MI SITIO
			System.out.println(this.getName() + " can't move to " + newpos.coordsStr() + " (already occupied position)");
		}
		
	}
	
	private int distancia(Casilla origen,Casilla destino)
	{
		int suma=0;
		int xorigen  = tablero.getCoordenadas(origen)[0];
		int yorigen  = tablero.getCoordenadas(origen)[1];
		int xdestino = tablero.getCoordenadas(destino)[0];
		int ydestino = tablero.getCoordenadas(destino)[1];
		suma += Math.abs(xorigen-xdestino);
		suma += Math.abs(yorigen-ydestino);
		
		return suma;
	}
	
	private void moverHaciaCasilla(Casilla c) {
		Casilla choice = null;
		int aux = 900;
		for (int i = 1; i < 4; i++) {
			for (int j = 1; j < 4; j++) {
				if (vision[i][j] != null) {
					if (vision[i][j].getContenido() == 0)
					{
						if(distancia(vision[i][j],c) < aux){
							choice = vision[i][j];
							aux = distancia(vision[i][j],c);
						}
							
				}
			}
		}
		}
		if(choice == null)
			niet();
		else move(choice);
		
	}
	
	private void navigate() {
		boolean hay = false;
		for (int i = 0; i < 5; i++) {
			for (int j = 0; j < 5; j++) {
				if (vision[i][j] != null) {
					if (vision[i][j].getContenido() == 0)
						hay = true;
				}
			}
		}
		if (hay) {
			int x, y;
			do {
				do {
					x = randInt(1,3);
					y = randInt(1,3);
				} while (  ( (x != 2) || (y != 2) ) && (vision[x][y] == null) );
			} while (vision[x][y].getContenido() != 0);
			move(vision[x][y]);
		} else {
			niet();
		}
	}
	private void niet() {
		System.out.println(this.getName() + " does nothing");	
	}	
	
	public static int randInt(int min, int max) {
	    Random rand = new Random();
	    int randomNum = rand.nextInt((max - min) + 1) + min;
	    return randomNum;
	}
	
	private boolean paqueteAdy() {
		boolean hay = false;
		for (int i = 1; i < 4; i++) {
			for (int j = 1; j < 4; j++) {
				if (vision[i][j] != null) {
					if ( (vision[i][j].getContenido() > 5) && (vision[i][j].getContenido() < 10) )
						hay = true;
				}
			}
		}
		return hay;
	}
	
	private void pick(Casilla from) {
		System.out.println(this.getName() + " tries to pick from " + from.coordsStr());
		if ( (this.carrying == 0) && ( (from.getContenido() > 5) || (from.getContenido() < 10) ) ) {
			//SI HAY UN PAQUETE Y ESTOY VACIO, LO PUEDO COGER
			this.energy = this.energy - 1;
			this.usedEnergy = this.usedEnergy + 1;
			this.carrying = from.getContenido();
			this.situation.setAgentLoad(this.carrying);
			this.situation.updateGui();
			memoria.remove(from.hashCode());
			from.setContenido(0);
			from.updateGui();
			System.out.println(this.getName() + " picks from " + from.coordsStr());
		} else {
			System.out.println(this.getName() + " can't pick from " + from.coordsStr() + " (nothing to pick)");
		}
		
	}
	
	private void moverHaciaPaquete() {
		int distX = 0, distY = 0;
		for (int i = 0; i < 5; i++) {
			for (int j = 0; j < 5; j++) {
				if (vision[i][j] != null) {
					if ( (vision[i][j].getContenido() > 5) && (vision[i][j].getContenido() < 10) ) {
						distX = i-2;
						distY = j-2;
						//destino = vision[i][j];
					}
				}
			}
		}
		if ( (distX == 0) && (distY == 0) ) return;
		else {
			if (distX == 0) {
				if (distY < 0) {
					if (vision[2][1].getContenido() == 0) move(vision[2][1]);
					else if (vision[1][1].getContenido() == 0) move(vision[1][1]);
					else if (vision[3][1].getContenido() == 0) move(vision[3][1]);
					else navigate();
				} else {
					if (vision[2][3].getContenido() == 0) move(vision[2][3]);
					else if (vision[1][3].getContenido() == 0) move(vision[1][3]);
					else if (vision[3][3].getContenido() == 0) move(vision[3][3]);
					else navigate();
				}			
			} else if (distY == 0) {
				if (distX < 0) {
					if (vision[1][2].getContenido() == 0) move(vision[1][2]);
					else if (vision[1][1].getContenido() == 0) move(vision[1][1]);
					else if (vision[1][3].getContenido() == 0) move(vision[1][3]);
					else navigate();
				} else {
					if (vision[3][2].getContenido() == 0) move(vision[3][2]);
					else if (vision[3][1].getContenido() == 0) move(vision[3][1]);
					else if (vision[3][3].getContenido() == 0) move(vision[3][3]);
					else navigate();
				}					
			} else if ( (distX < 0) && (distY < 0) ) {
				if (vision[1][1].getContenido() == 0) move(vision[1][1]);
				else if (vision[1][2].getContenido() == 0) move(vision[1][2]);
				else if (vision[2][1].getContenido() == 0) move(vision[2][1]);
				else navigate();
			} else if ( (distX < 0) && (distY > 0) ) {
				if (vision[1][3].getContenido() == 0) move(vision[1][3]);
				else if (vision[1][2].getContenido() == 0) move(vision[1][2]);
				else if (vision[2][3].getContenido() == 0) move(vision[2][3]);
				else navigate();
			} else if ( (distX > 0) && (distY < 0) ) {
				if (vision[3][1].getContenido() == 0) move(vision[3][1]);
				else if (vision[3][2].getContenido() == 0) move(vision[3][2]);
				else if (vision[2][1].getContenido() == 0) move(vision[3][1]);
				else navigate();
			} else if ( (distX > 0) && (distY > 0) ) {
				if (vision[3][3].getContenido() == 0) move(vision[3][3]);
				else if (vision[3][2].getContenido() == 0) move(vision[3][2]);
				else if (vision[2][3].getContenido() == 0) move(vision[2][3]);
				else navigate();
			}
			
		}
	}
	
	
	private void cogerPaquete() {
		int x = 0, y = 0;
		for (int i = 1; i < 4; i++) {
			for (int j = 1; j < 4; j++) {
				if (vision[i][j] != null) {
					if ( (vision[i][j].getContenido() > 5) && (vision[i][j].getContenido() < 10) ) {
						x = i;
						y = j;
					}
				}
			}
		}
		pick(vision[x][y]);
		
	}
	
	private boolean paqueteVis() {
		boolean hay = false;
		for (int i = 0; i < 5; i++) {
			for (int j = 0; j < 5; j++) {
				if (vision[i][j] != null) {
					if ( (vision[i][j].getContenido() > 5) && (vision[i][j].getContenido() < 10) )
						hay = true;
				}
			}
		}
		return hay;
	}
	
	private void depositarDondeSea() {
		boolean hay = false;
		for (int i = 0; i < 5; i++) {
			for (int j = 0; j < 5; j++) {
				if (vision[i][j] != null) {
					if (vision[i][j].getContenido() == 0)
						hay = true;
				}
			}
		}
		if (hay) {
			int x, y;
			do {
				do {
					x = randInt(1,3);
					y = randInt(1,3);
				} while (  ( (x != 2) || (y != 2) ) && (vision[x][y] == null) );
			} while (vision[x][y].getContenido() != 0);
			depo(vision[x][y]);
		} else {
			niet();
		}
	}
	
	private void depo(Casilla into) {
		System.out.println(this.getName() + " tries to deposit in " + into.coordsStr());
		if (into.getContenido() == 0) {
			//SI NO HAY NADA, LO PUEDO DEJAR
			this.energy = this.energy - 1;
			this.usedEnergy = this.usedEnergy + 1;
			into.setContenido(this.carrying);
			into.updateGui();
			this.carrying = 0;
			this.situation.setAgentLoad(0);
			this.situation.updateGui();
			memoria.put(into.hashCode(), into);
			System.out.println(this.getName() + " deposits in " + into.coordsStr());
		} else {
			if (into.getContenido() == this.carrying - 4) {
				//SI LO QUE HAY ES UN PORTAL, LO PUEDO DEJAR
				this.energy = this.energy - 1;
				this.usedEnergy = this.usedEnergy + 1;
				this.carrying = 0;
				this.situation.setAgentLoad(0);
				this.situation.updateGui();
				this.tablero.onePacketLess();
				System.out.println(this.getName() + " deposits in portal at " + into.coordsStr());
			} else {
				//SI HAY OTRA COSA, NO PUEDO HACER NADA
				System.out.println(this.getName() + " can't deposit in " + into.coordsStr() + " (already occupied position)");
			}
		}
	}
	
	private void depositarPortal() {
		int x = 0, y = 0;
		for (int i = 1; i < 4; i++) {
			for (int j = 1; j < 4; j++) {
				if (vision[i][j] != null) {
					if ( (vision[i][j].getContenido()) == (carrying - 4)) {
						x = i;
						y = j;
					}
				}
			}
		}
		depo(vision[x][y]);
		
	}
	
	private boolean cargadorVis()
	{
		boolean hay = false;
		for (int i = 0; i < 5; i++) {
			for (int j = 0; j < 5; j++) {
				if (vision[i][j] != null) {
					if ( ((vision[i][j].getContenido()) == CARGADOR)||((vision[i][j].getContenido()) == AGENTECARGADOR) )
						hay = true;
				}
			}
		}
		return hay;
	}
	
	private boolean portalCorrectoVis() {
		boolean hay = false;
		for (int i = 0; i < 5; i++) {
			for (int j = 0; j < 5; j++) {
				if (vision[i][j] != null) {
					if ( (vision[i][j].getContenido()) == (carrying - 4) )
						hay = true;
				}
			}
		}
		return hay;
	}
	
	
	private int destinoVisibleyValido() {
		int result = 0;
		for (int i = 0; i < 5; i++) {
			for (int j = 0; j < 5; j++) {
				if (vision[i][j] != null) {
					if ( tablero.getCoordenadas(vision[i][j])==tablero.getCoordenadas(destino))
						if(vision[i][j].getContenido()>5 && vision[i][j].getContenido()<10)
							result = 1;
						else result = 2;
				}
			}
		}
		return result;
	}
	

	
	private boolean portalCorrectoAdy() {
		boolean hay = false;
		for (int i = 1; i < 4; i++) {
			for (int j = 1; j < 4; j++) {
				if (vision[i][j] != null) {
					if ( (vision[i][j].getContenido()) == (carrying - 4))
						hay = true;
				}
			}
		}
		return hay;
	}
	
	private void load() {
		System.out.println(this.getName() + " tries to load in " + this.situation.coordsStr());
		if (this.situation.hasLoader()) {
			//SI HAY UN CARGADOR
			if (this.energy < this.maxenergy) {
				//Y LA BATERIA NO ESTA AL MAXIMO, CARGO
				this.energy = this.energy + 1;
				System.out.println(this.getName() + " loads");
				if (this.energy == this.maxenergy) {
					System.out.println(this.getName() + " load complete");
				}
			} else {
				//BATERIA MAXIMA, NO CARGO
				System.out.println(this.getName() + " can't load (battery already full)");
			}
		} else {
			//NO HAY CARGADOR
			System.out.println(this.getName() + " can't load (no loader at current position)");
		}
	}
	
	private boolean cargadorOcupado()
	{
		boolean hay = false;
		for (int i = 0; i < 5; i++) {
			for (int j = 0; j < 5; j++) {
				if (vision[i][j] != null) {
					if ( (vision[i][j].getGradiente() == 0) && (vision[i][j].getContenido()!= CARGADOR ))
						return true;
					else return false;
				}
			}
		}
		return hay;
	}
	
	private void moverHaciaGradiente() {
		int ref = situation.getGradiente();
		int mej = 0;
		int x = 0, y = 0;
		for (int i = 0; i < 5; i++) {
			for (int j = 0; j < 5; j++) {
				if (vision[i][j] != null) {
					if ( (vision[i][j].getGradiente() < situation.getGradiente() ) && (vision[i][j].getContenido() < 2) ) {
						x = i;
						y = j;
						mej = ref - vision[i][j].getGradiente();
					}
				}
			}
		}
		//SI HAY GRADIENTE MEJOR, ME MUEVO, SINO, NO
		if (mej > 0) move(vision[x][y]);
		else niet();
		
	}
	
	public void takeDown() {
		//TELL ENERGY USE
		System.out.println("Taking down agent " + this.getName());
		System.out.println(this.getName() + " used " + this.usedEnergy + " energy units");
		this.situation.setAgentLoad(0);
		this.situation.setContenido(0);
		this.situation.updateGui();
		tablero.addUsedEnergy(this.usedEnergy);
	}

	@Override
	protected void setup() {
		Object[] args = getArguments();
		tablero = (Tablero) args[0];
		this.tickSpeed = (Integer) args[1];
		situation = tablero.requestStartPosition(this);
		state = 0;
		carrying = 0;
		maxenergy = tablero.getSize();
		energy = maxenergy;
		vision = tablero.getVision(situation);
		checkEnviroment();
		addBehaviour(new TickerBehaviour(this, this.tickSpeed) {
			
			@Override
			protected void onTick() {
				if(energy<=0)
					System.out.println("MUERTO");
				if (tablero.getActivePackages() == 0) {
					System.out.println("END");
					doDelete();
				}
				vision=tablero.getVision(situation);
				checkEnviroment();
				switch (state) {
				
				case INICIAL:
					if(paqueteAdy()){
						cogerPaquete();
						if(memoria.containsKey(carrying-4))
							state = LLEVANDOPAQUETEAPUNTO;
						else state = LLEVANDOPAQUETE;
					}else if(paqueteVis()){
						moverHaciaPaquete();
						state = YENDOAPAQUETE;
						}else{
							navigate();
							state = BUSCANDO;				
							}
					break;
					
				case LLEVANDOPAQUETE:
					System.out.println("LLEVANDOPAQUETE");
					if(situation.getGradiente() + 3 >= energy){
						depositarDondeSea();
						if (situation.getGradiente() <= 2)
							state = CARGADORAVISTADO;
						else state = YENDOACARGADOR;
					} else if(memoria.containsKey(carrying-4)) {
							moverHaciaCasilla(memoria.get(carrying-4));
							state = LLEVANDOPAQUETEAPUNTO;
							}else navigate();
					
					break;
				
				case LLEVANDOPAQUETEAPUNTO:
					System.out.println("LLEVANDOPAQUETEAPUNTO");
					if(situation.getGradiente()+3>=energy){
						depositarDondeSea();
						if(situation.getGradiente()<=2)
							state = CARGADORAVISTADO;
						else state = YENDOACARGADOR;
					}else if(portalCorrectoAdy()){
						depositarPortal();
						if (tablero.getActivePackages() == 0) {
							System.out.println("END");
							doDelete();
						}else state = BUSCANDO;
					} else moverHaciaCasilla(memoria.get(carrying-4));
				break;
				
				
				case YENDOACARGADOR:
					System.out.println("YENDOACARGADOR");
					moverHaciaGradiente();
					if(cargadorVis())
						state = CARGADORAVISTADO;
					break;
					
				case CARGADORAVISTADO:
					System.out.println("CARGADORAVISTADO");
					if(situation.hasLoader()){
						load();
						state = CARGANDO;
					}
					if(cargadorOcupado())
						niet();
					else moverHaciaGradiente();
					break;
					
				case CARGANDO:
					System.out.println("CARGANDO ENERGY:" + energy);
					if(energy==maxenergy)
					{
						if(carrying!=0){
							if(memoria.containsKey(carrying-4)){
								moverHaciaCasilla(memoria.get(carrying-4));
								state = LLEVANDOPAQUETEAPUNTO;
							}else{ 
								navigate();
								state = LLEVANDOPAQUETE;
							}
						} else if(paqueteAdy()){
									cogerPaquete();
									if(memoria.containsKey(carrying-4))
										state = LLEVANDOPAQUETEAPUNTO;
									else state = LLEVANDOPAQUETE;
								}else if(paqueteVis()){
									moverHaciaPaquete();
									state = YENDOAPAQUETE;
								}else{
									destino = firstPacket();
									if(destino==null)
									{
										navigate();
										state = BUSCANDO;
									}else {
										state = YENDOAPAQUETE;
										moverHaciaCasilla(destino);
									}
								}
							 
						
					}else energy++;
					break;
					
				case YENDOAPAQUETE:
					System.out.println("YENDOAPAQUETE");
					if(situation.getGradiente()+2>=energy){
						state = YENDOACARGADOR;
						moverHaciaGradiente();
					
					}else{	
						if(paqueteAdy()){
							destino = null;
							cogerPaquete();
							if(memoria.containsKey(carrying-4))
								state = LLEVANDOPAQUETEAPUNTO;
							else state = LLEVANDOPAQUETE;
						}else if(paqueteVis()){
							moverHaciaPaquete();
							state = YENDOAPAQUETE;
						} else
							{
							int aux = destinoVisibleyValido();
							if(aux!=1)
								moverHaciaCasilla(destino);
							else {
									
									memoria.remove(destino.hashCode());
									destino=null;
									destino = firstPacket();
									if(destino==null){
										state = BUSCANDO;
										navigate();
									}else {
										state = YENDOAPAQUETE;
										moverHaciaCasilla(destino);
									}
										
								 }
							}
					}
					break;
				
				case BUSCANDO:
					System.out.println("BUSCANDO");
					if(situation.getGradiente()+2>=energy){
						state = YENDOACARGADOR;
						moverHaciaGradiente();
						
					}else {
					destino = firstPacket();
					if(destino==null)
						navigate();
					else state = YENDOAPAQUETE;
					}
				default:
					break;
				}
			}
		});
	}

}


